﻿using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace Sidvall.IO
{
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling")]
    public class StorageManager
    {
        #region Public Members

        public IStorage Storage { get; set; }

        #region Combine

        public string Combine(params string[] paths)
        {
            return this.Storage.Combine(paths);
        }

        #endregion
        #region GetFileSystemEntryName

        public string GetFileSystemEntryName(string path)
        {
            return StorageGroupManager.GetFileSystemEntryName(path, this.Storage.FolderDelimiter);
        }

        #endregion
        #region GetFolderPath

        public string GetFolderPath(FolderType folder)
        {
            return this.Storage.GetFolderPath(folder);
        }

        #endregion
        #region GetTemporaryFilePath

        public string GetTemporaryFilePath()
        {
            return GetTemporaryFilePath(null);
        }
        public string GetTemporaryFilePath(string extension)
        {
            var path = GetFolderPath(Sidvall.IO.FolderType.TemporaryApplicationData);
            path = GetPath(path, System.IO.Path.GetRandomFileName());
            if (!string.IsNullOrWhiteSpace(extension))
                path = System.IO.Path.ChangeExtension(path, extension);
            return path;
        }

        #endregion
        #region GetPath

        public string GetPath(string path)
        {
            return this.Storage.GetPath(path);
        }

        #endregion
        #region GetStoragePath

        public string GetStoragePath(string path)
        {
            return this.Storage.GetStoragePath(path);
        }

        #endregion

        #region CreateFolderAsync

        public async Task<bool> CreateFolderAsync(string path)
        {
            return await CreateFolderAsync(path, CancellationToken.None).ConfigureAwait(false);
        }
        public async Task<bool> CreateFolderAsync(string path, CancellationToken cancellationToken)
        {
            return await this.Storage.CreateFolderAsync(path, cancellationToken).ConfigureAwait(false);
        }

        #endregion

        #region DeleteFileAsync

        public async Task<bool> DeleteFileAsync(string path)
        {
            return await DeleteFileAsync(path, CancellationToken.None).ConfigureAwait(false);
        }
        public async Task<bool> DeleteFileAsync(string path, CancellationToken cancellationToken)
        {
            return await this.Storage.DeleteFileAsync(path, cancellationToken).ConfigureAwait(false);
        }

        #endregion
        #region DeleteFolderAsync

        public async Task<bool> DeleteFolderAsync(string path, bool recursive)
        {
            return await DeleteFolderAsync(path, recursive, CancellationToken.None).ConfigureAwait(false);
        }
        public async Task<bool> DeleteFolderAsync(string path, bool recursive, CancellationToken cancellationToken)
        {
            return await this.Storage.DeleteFolderAsync(path, recursive, cancellationToken).ConfigureAwait(false);
        }

        #endregion

        #region FileExistsAsync

        public async Task<bool> FileExistsAsync(string path)
        {
            return await FileExistsAsync(path, CancellationToken.None).ConfigureAwait(false);
        }
        public async Task<bool> FileExistsAsync(string path, CancellationToken cancellationToken)
        {
            return await this.Storage.FileExistsAsync(path, cancellationToken).ConfigureAwait(false);
        }

        #endregion
        #region FolderExistsAsync

        public async Task<bool> FolderExistsAsync(string path)
        {
            return await FolderExistsAsync(path, CancellationToken.None).ConfigureAwait(false);
        }
        public async Task<bool> FolderExistsAsync(string path, CancellationToken cancellationToken)
        {
            return await this.Storage.FolderExistsAsync(path, cancellationToken).ConfigureAwait(false);
        }

        #endregion

        #region GetFileNamesAsync

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")]
        public async Task<IEnumerable<string>> GetFileNamesAsync(string path, IEnumerable<string> searchPatterns, SearchOption searchOption)
        {
            return await GetFileNamesAsync(path, searchPatterns, searchOption, CancellationToken.None).ConfigureAwait(false);
        }
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")]
        public async Task<IEnumerable<string>> GetFileNamesAsync(string path, IEnumerable<string> searchPatterns, SearchOption searchOption,
            CancellationToken cancellationToken)
        {
            IEnumerable<string> names;
            IEnumerable<string> nameList;

            nameList = null;
            if (searchPatterns == null)
                searchPatterns = new string[1] { DefaultSearchPattern };
            foreach (var searchPattern in searchPatterns)
            {
                cancellationToken.ThrowIfCancellationRequested();
                if (!string.IsNullOrWhiteSpace(searchPattern))
                    names = await this.Storage.GetFileNamesAsync(path, searchPattern, searchOption, cancellationToken).ConfigureAwait(false);
                else
                    names = await this.Storage.GetFileNamesAsync(path, DefaultSearchPattern, searchOption, cancellationToken).ConfigureAwait(false);
                if (nameList == null)
                    nameList = names;
                else
                    nameList = nameList.Concat(names);
            }
            return nameList;
        }

        #endregion
        #region GetFolderNamesAsync

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")]
        public async Task<IEnumerable<string>> GetFolderNamesAsync(string path, IEnumerable<string> searchPatterns, SearchOption searchOption)
        {
            return await GetFolderNamesAsync(path, searchPatterns, searchOption, CancellationToken.None).ConfigureAwait(false);
        }
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")]
        public async Task<IEnumerable<string>> GetFolderNamesAsync(string path, IEnumerable<string> searchPatterns, SearchOption searchOption,
            CancellationToken cancellationToken)
        {
            IEnumerable<string> names;
            IEnumerable<string> nameList;

            nameList = null;
            if (searchPatterns == null)
                searchPatterns = new string[1] { DefaultSearchPattern };
            foreach (var searchPattern in searchPatterns)
            {
                cancellationToken.ThrowIfCancellationRequested();
                if (!string.IsNullOrWhiteSpace(searchPattern))
                    names = await this.Storage.GetFolderNamesAsync(path, searchPattern, searchOption, cancellationToken).ConfigureAwait(false);
                else
                    names = await this.Storage.GetFolderNamesAsync(path, DefaultSearchPattern, searchOption, cancellationToken).ConfigureAwait(false);
                if (nameList == null)
                    nameList = names;
                else
                    nameList = nameList.Concat(names);
            }
            return nameList;
        }

        #endregion
        #region GetFileSystemEntryNamesAsync

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")]
        public async Task<IEnumerable<string>> GetFileSystemEntryNamesAsync(string path, IEnumerable<string> searchPatterns, SearchOption searchOption)
        {
            return await GetFileSystemEntryNamesAsync(path, searchPatterns, searchOption, CancellationToken.None).ConfigureAwait(false);
        }
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")]
        public async Task<IEnumerable<string>> GetFileSystemEntryNamesAsync(string path, IEnumerable<string> searchPatterns, SearchOption searchOption,
            CancellationToken cancellationToken)
        {
            IEnumerable<string> names;
            IEnumerable<string> nameList;

            nameList = null;
            if (searchPatterns == null)
                searchPatterns = new string[1] { DefaultSearchPattern };
            foreach (var searchPattern in searchPatterns)
            {
                cancellationToken.ThrowIfCancellationRequested();
                if (!string.IsNullOrWhiteSpace(searchPattern))
                    names = await this.Storage.GetFileSystemEntryNamesAsync(path, searchPattern, searchOption, cancellationToken).ConfigureAwait(false);
                else
                    names = await this.Storage.GetFileSystemEntryNamesAsync(path, DefaultSearchPattern, searchOption, cancellationToken).ConfigureAwait(false);
                if (nameList == null)
                    nameList = names;
                else
                    nameList = nameList.Concat(names);
            }
            return nameList;
        }

        #endregion

        #region GetFileAsync

        public async Task<File> GetFileAsync(string path, bool loadFileDetails)
        {
            return await GetFileAsync(path, loadFileDetails, CancellationToken.None).ConfigureAwait(false);
        }
        public async Task<File> GetFileAsync(string path, bool loadFileDetails, CancellationToken cancellationToken)
        {
            return await this.Storage.GetFileAsync(path, loadFileDetails, cancellationToken).ConfigureAwait(false);
        }

        #endregion
        #region GetFilesAsync

        public async Task<FileCollection> GetFilesAsync(string path, IEnumerable<string> searchPatterns, SearchOption searchOption, bool loadFileDetails)
        {
            return await GetFilesAsync(path, searchPatterns, searchOption, loadFileDetails, CancellationToken.None).ConfigureAwait(false);
        }
        public async Task<FileCollection> GetFilesAsync(string path, IEnumerable<string> searchPatterns, SearchOption searchOption, bool loadFileDetails,
            CancellationToken cancellationToken)
        {
            FileCollection items;
            IEnumerable<File> result;

            items = new FileCollection();
            if (searchPatterns == null)
                searchPatterns = new string[1] { DefaultSearchPattern };
            foreach (var searchPattern in searchPatterns)
            {
                cancellationToken.ThrowIfCancellationRequested();
                if (!string.IsNullOrWhiteSpace(searchPattern))
                    result = await this.Storage.GetFilesAsync(path, searchPattern, searchOption, loadFileDetails, cancellationToken).ConfigureAwait(false);
                else
                    result = await this.Storage.GetFilesAsync(path, DefaultSearchPattern, searchOption, loadFileDetails, cancellationToken).ConfigureAwait(false);
                items.AddRange(result);
            }
            return items;
        }

        #endregion
        #region GetFolderAsync

        public async Task<Folder> GetFolderAsync(string path, bool loadFolderDetails)
        {
            return await GetFolderAsync(path, loadFolderDetails, CancellationToken.None).ConfigureAwait(false);
        }
        public async Task<Folder> GetFolderAsync(string path, bool loadFolderDetails, CancellationToken cancellationToken)
        {
            return await this.Storage.GetFolderAsync(path, loadFolderDetails, cancellationToken).ConfigureAwait(false);
        }

        #endregion
        #region GetFoldersAsync

        public async Task<FolderCollection> GetFoldersAsync(string path, IEnumerable<string> searchPatterns, SearchOption searchOption, bool loadFolderDetails)
        {
            return await GetFoldersAsync(path, searchPatterns, searchOption, loadFolderDetails, CancellationToken.None).ConfigureAwait(false);
        }
        public async Task<FolderCollection> GetFoldersAsync(string path, IEnumerable<string> searchPatterns, SearchOption searchOption, bool loadFolderDetails,
            CancellationToken cancellationToken)
        {
            FolderCollection items;
            IEnumerable<Folder> result;

            items = new FolderCollection();
            if (searchPatterns == null)
                searchPatterns = new string[1] { DefaultSearchPattern };
            foreach (var searchPattern in searchPatterns)
            {
                cancellationToken.ThrowIfCancellationRequested();
                if (!string.IsNullOrWhiteSpace(searchPattern))
                    result = await this.Storage.GetFoldersAsync(path, searchPattern, searchOption, loadFolderDetails, cancellationToken).ConfigureAwait(false);
                else
                    result = await this.Storage.GetFoldersAsync(path, DefaultSearchPattern, searchOption, loadFolderDetails, cancellationToken).ConfigureAwait(false);
                items.AddRange(result);
            }
            return items;
        }

        #endregion

        #region OpenFileAsync

        public async Task<System.IO.Stream> OpenFileAsync(string path, FileMode mode, FileAccess access)
        {
            return await OpenFileAsync(path, mode, access, CancellationToken.None).ConfigureAwait(false);
        }
        public async Task<System.IO.Stream> OpenFileAsync(string path, FileMode mode, FileAccess access, CancellationToken cancellationToken)
        {
            return await this.Storage.OpenFileAsync(path, mode, access, cancellationToken).ConfigureAwait(false);
        }

        #endregion
        #region OpenFileStreamReader

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
        public System.IO.StreamReader OpenFileStreamReader(System.IO.Stream stream, System.Text.Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize)
        {
            return new System.IO.StreamReader(stream, encoding, detectEncodingFromByteOrderMarks, bufferSize, false);
        }

        #endregion
        #region OpenFileStreamReaderAsync

        public async Task<System.IO.StreamReader> OpenFileStreamReaderAsync(string path)
        {
            return await OpenFileStreamReaderAsync(path, CancellationToken.None).ConfigureAwait(false);
        }
        public async Task<System.IO.StreamReader> OpenFileStreamReaderAsync(string path, CancellationToken cancellationToken)
        {
            return await OpenFileStreamReaderAsync(path, System.Text.UTF8Encoding.UTF8, true, this.Storage.DefaultBufferSize, cancellationToken).ConfigureAwait(false);
        }
        public async Task<System.IO.StreamReader> OpenFileStreamReaderAsync(string path, System.Text.Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize)
        {
            return await OpenFileStreamReaderAsync(path, encoding, detectEncodingFromByteOrderMarks, bufferSize, CancellationToken.None).ConfigureAwait(false);
        }
        public async Task<System.IO.StreamReader> OpenFileStreamReaderAsync(string path, System.Text.Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize,
            CancellationToken cancellationToken)
        {
            var stream = await this.Storage.OpenFileAsync(path, FileMode.Open, FileAccess.Read, cancellationToken).ConfigureAwait(false);
            try
            {
                return OpenFileStreamReader(stream, encoding, detectEncodingFromByteOrderMarks, bufferSize);
            }
            catch
            {
                stream.Dispose();
                throw;
            }
        }

        #endregion
        #region OpenFileStreamWriterAsync

        public async Task<System.IO.StreamWriter> OpenFileStreamWriterAsync(string path)
        {
            return await OpenFileStreamWriterAsync(path, CancellationToken.None).ConfigureAwait(false);
        }
        public async Task<System.IO.StreamWriter> OpenFileStreamWriterAsync(string path, CancellationToken cancellationToken)
        {
            return await OpenFileStreamWriterAsync(path, System.Text.UTF8Encoding.UTF8, this.Storage.DefaultBufferSize, cancellationToken).ConfigureAwait(false);
        }
        public async Task<System.IO.StreamWriter> OpenFileStreamWriterAsync(string path, System.Text.Encoding encoding, int bufferSize)
        {
            return await OpenFileStreamWriterAsync(path, encoding, bufferSize, CancellationToken.None).ConfigureAwait(false);
        }
        public async Task<System.IO.StreamWriter> OpenFileStreamWriterAsync(string path, System.Text.Encoding encoding, int bufferSize, CancellationToken cancellationToken)
        {
            var stream = await this.Storage.OpenFileAsync(path, FileMode.Create, FileAccess.Write, cancellationToken).ConfigureAwait(false);
            try
            {
                return new System.IO.StreamWriter(stream, encoding, bufferSize, false);
            }
            catch
            {
                stream.Dispose();
                throw;
            }
        }

        #endregion

        #region ReadAllBytesAsync

        public async Task<byte[]> ReadAllBytesAsync(string path)
        {
            return await ReadAllBytesAsync(path, this.Storage.DefaultBufferSize).ConfigureAwait(false);
        }
        public async Task<byte[]> ReadAllBytesAsync(string path, int bufferSize)
        {
            return await ReadAllBytesAsync(path, bufferSize, null, CancellationToken.None).ConfigureAwait(false);
        }
        public async Task<byte[]> ReadAllBytesAsync(string path, int bufferSize, System.IProgress<FileProgress> progress, CancellationToken cancellationToken)
        {
            using (var stream = await OpenFileAsync(path, FileMode.Open, FileAccess.Read, cancellationToken).ConfigureAwait(false))
            {
                return await ReadAllBytesAsync(stream, bufferSize, progress, cancellationToken).ConfigureAwait(false);
            }
        }

        public async Task<byte[]> ReadAllBytesAsync(System.IO.Stream stream)
        {
            return await ReadAllBytesAsync(stream, this.Storage.DefaultBufferSize).ConfigureAwait(false);
        }
        public async Task<byte[]> ReadAllBytesAsync(System.IO.Stream stream, int bufferSize)
        {
            return await ReadAllBytesAsync(stream, bufferSize, null, CancellationToken.None).ConfigureAwait(false);
        }
        public async Task<byte[]> ReadAllBytesAsync(System.IO.Stream stream, int bufferSize, System.IProgress<FileProgress> progress, CancellationToken cancellationToken)
        {
            int count;

            var buffer = new byte[bufferSize];
            using (System.IO.MemoryStream ms = new System.IO.MemoryStream())
            {
                while ((count = await stream.ReadAsync(buffer, 0, bufferSize, cancellationToken).ConfigureAwait(false)) > 0)
                {
                    cancellationToken.ThrowIfCancellationRequested();
                    await ms.WriteAsync(buffer, 0, count, cancellationToken).ConfigureAwait(false);
                    if (progress != null)
                    {
                        var context = new FileProgress()
                        {
                            Length = stream.Length,
                            Position = ms.Position,
                        };
                        progress.Report(context);
                    }
                }
                return ms.ToArray();
            }
        }

        #endregion
        #region ReadAllTextAsync

        public async Task<string> ReadAllTextAsync(string path)
        {
            return await ReadAllTextAsync(path, System.Text.UTF8Encoding.UTF8, true, this.Storage.DefaultBufferSize).ConfigureAwait(false);
        }
        public async Task<string> ReadAllTextAsync(string path, System.Text.Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize)
        {
            return await ReadAllTextAsync(path, encoding, detectEncodingFromByteOrderMarks, bufferSize, null, CancellationToken.None).ConfigureAwait(false);
        }
        public async Task<string> ReadAllTextAsync(string path, System.Text.Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize,
            System.IProgress<FileProgress> progress, CancellationToken cancellationToken)
        {
            using (var stream = await OpenFileAsync(path, FileMode.Open, FileAccess.Read, cancellationToken).ConfigureAwait(false))
            {
                return await ReadAllTextAsync(stream, encoding, detectEncodingFromByteOrderMarks, bufferSize, progress, cancellationToken).ConfigureAwait(false);
            }
        }

        public async Task<string> ReadAllTextAsync(System.IO.Stream stream)
        {
            return await ReadAllTextAsync(stream, System.Text.UTF8Encoding.UTF8, true, this.Storage.DefaultBufferSize).ConfigureAwait(false);
        }
        public async Task<string> ReadAllTextAsync(System.IO.Stream stream, System.Text.Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize)
        {
            return await ReadAllTextAsync(stream, encoding, detectEncodingFromByteOrderMarks, bufferSize, null, CancellationToken.None).ConfigureAwait(false);
        }
        public async Task<string> ReadAllTextAsync(System.IO.Stream stream, System.Text.Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize,
            System.IProgress<FileProgress> progress, CancellationToken cancellationToken)
        {
            int count;

            var sb = new System.Text.StringBuilder();
            var buffer = new char[bufferSize];
            using (var reader = OpenFileStreamReader(stream, encoding, detectEncodingFromByteOrderMarks, bufferSize))
            {
                while ((count = await reader.ReadAsync(buffer, 0, bufferSize).ConfigureAwait(false)) > 0)
                {
                    cancellationToken.ThrowIfCancellationRequested();
                    sb.Append(buffer, 0, count);
                    if (progress != null)
                    {
                        var context = new FileProgress()
                        {
                            Length = reader.BaseStream.Length,
                            Position = sb.Length,
                        };
                        progress.Report(context);
                    }
                }
            }
            return sb.ToString();
        }

        #endregion

        #region WriteAllBytesAsync

        public async Task WriteAllBytesAsync(string path, byte[] content)
        {
            await WriteAllBytesAsync(path, content, this.Storage.DefaultBufferSize).ConfigureAwait(false);
        }
        public async Task WriteAllBytesAsync(string path, byte[] content, int bufferSize)
        {
            await WriteAllBytesAsync(path, content, bufferSize, null, CancellationToken.None).ConfigureAwait(false);
        }
        public async Task WriteAllBytesAsync(string path, byte[] content, int bufferSize, System.IProgress<FileProgress> progress, CancellationToken cancellationToken)
        {
            int offset, count;

            offset = 0;
            using (var stream = await OpenFileAsync(path, FileMode.Create, FileAccess.Write, cancellationToken).ConfigureAwait(false))
            {
                do
                {
                    cancellationToken.ThrowIfCancellationRequested();
                    count = content.Length - offset;
                    if (count > bufferSize)
                        count = bufferSize;
                    if (count > 0)
                    {
                        await stream.WriteAsync(content, offset, count, cancellationToken).ConfigureAwait(false);
                        offset += count;
                    }
                    else
                    {
                        await stream.FlushAsync(cancellationToken).ConfigureAwait(false);
                    }
                    if (progress != null)
                    {
                        var context = new FileProgress()
                        {
                            Length = content.Length,
                            Position = offset,
                        };
                        progress.Report(context);
                    }
                } while (count > 0);
            }
        }

        #endregion
        #region WriteAllTextAsync

        public async Task WriteAllTextAsync(string path, string content)
        {
            await WriteAllTextAsync(path, content, System.Text.UTF8Encoding.UTF8, this.Storage.DefaultBufferSize).ConfigureAwait(false);
        }
        public async Task WriteAllTextAsync(string path, string content, System.Text.Encoding encoding, int bufferSize)
        {
            await WriteAllTextAsync(path, content, encoding, bufferSize, null, CancellationToken.None).ConfigureAwait(false);
        }
        public async Task WriteAllTextAsync(string path, string content, System.Text.Encoding encoding, int bufferSize,
             System.IProgress<FileProgress> progress, CancellationToken cancellationToken)
        {
            char[] buffer;
            int offset, count;

            offset = 0;
            using (var writer = await OpenFileStreamWriterAsync(path, encoding, bufferSize, cancellationToken).ConfigureAwait(false))
            {
                do
                {
                    cancellationToken.ThrowIfCancellationRequested();
                    count = content.Length - offset;
                    if (count > bufferSize)
                        count = bufferSize;
                    if (count > 0)
                    {
                        buffer = content.ToCharArray(offset, count);
                        await writer.WriteAsync(buffer, 0, count).ConfigureAwait(false);
                        offset += count;
                    }
                    else
                    {
                        await writer.FlushAsync().ConfigureAwait(false);
                    }
                    if (progress != null)
                    {
                        var context = new FileProgress()
                        {
                            Length = content.Length,
                            Position = offset,
                        };
                        progress.Report(context);
                    }
                } while (count > 0);
            }
        }

        #endregion
        #region WriteStreamAsync

        public async Task WriteStreamAsync(string path, System.IO.Stream content)
        {
            await WriteStreamAsync(path, content, this.Storage.DefaultBufferSize).ConfigureAwait(false);
        }
        public async Task WriteStreamAsync(string path, System.IO.Stream content, int bufferSize)
        {
            await WriteStreamAsync(path, content, bufferSize, null, CancellationToken.None).ConfigureAwait(false);
        }
        public async Task WriteStreamAsync(string path, System.IO.Stream content, int bufferSize, System.IProgress<FileProgress> progress, CancellationToken cancellationToken)
        {
            byte[] buffer;
            long position;
            int count;

            if (content.CanSeek)
            {
                if (content.Position != 0)
                    content.Position = 0;
            }
            position = 0;
            buffer = new byte[bufferSize];
            using (var stream = await OpenFileAsync(path, FileMode.Create, FileAccess.Write, cancellationToken).ConfigureAwait(false))
            {
                do
                {
                    count = await content.ReadAsync(buffer, 0, bufferSize, cancellationToken).ConfigureAwait(false);
                    if (count > 0)
                    {
                        cancellationToken.ThrowIfCancellationRequested();
                        await stream.WriteAsync(buffer, 0, count, cancellationToken).ConfigureAwait(false);
                        if (progress != null)
                        {
                            position += count;
                            var context = new FileProgress()
                            {
                                Length = content.Length,
                                Position = position,
                            };
                            progress.Report(context);
                        }
                    }
                    else
                    {
                        await stream.FlushAsync(cancellationToken).ConfigureAwait(false);
                    }
                } while (count > 0);
            }
        }

        #endregion

        #region CopyFileAsync

        public async Task<bool> CopyFileAsync(string sourceFileName, string destinationFileName, bool deleteExistingFile)
        {
            return await CopyFileAsync(sourceFileName, destinationFileName, deleteExistingFile, CancellationToken.None).ConfigureAwait(false);
        }
        public async Task<bool> CopyFileAsync(string sourceFileName, string destinationFileName, bool deleteExistingFile, CancellationToken cancellationToken)
        {
            return await this.Storage.CopyFileAsync(sourceFileName, destinationFileName, deleteExistingFile, cancellationToken).ConfigureAwait(false);
        }

        #endregion
        #region MoveFileAsync

        public async Task<bool> MoveFileAsync(string sourceFileName, string destinationFileName, bool deleteExistingFile)
        {
            return await MoveFileAsync(sourceFileName, destinationFileName, deleteExistingFile, CancellationToken.None).ConfigureAwait(false);
        }
        public async Task<bool> MoveFileAsync(string sourceFileName, string destinationFileName, bool deleteExistingFile, CancellationToken cancellationToken)
        {
            return await this.Storage.MoveFileAsync(sourceFileName, destinationFileName, deleteExistingFile, cancellationToken).ConfigureAwait(false);
        }

        #endregion
        #region MoveFolderAsync

        public async Task<bool> MoveFolderAsync(string sourceFolderName, string destinationFolderName, bool deleteExistingFolder)
        {
            return await MoveFolderAsync(sourceFolderName, destinationFolderName, deleteExistingFolder, CancellationToken.None).ConfigureAwait(false);
        }
        public async Task<bool> MoveFolderAsync(string sourceFolderName, string destinationFolderName, bool deleteExistingFolder, CancellationToken cancellationToken)
        {
            return await this.Storage.MoveFolderAsync(sourceFolderName, destinationFolderName, deleteExistingFolder, cancellationToken).ConfigureAwait(false);
        }

        #endregion
        #region RenameFileAsync

        public async Task RenameFileAsync(string sourceFileName, string oldName, string newName)
        {
            await RenameFileAsync(sourceFileName, oldName, newName, CancellationToken.None).ConfigureAwait(false);
        }
        public async Task RenameFileAsync(string sourceFileName, string oldName, string newName, CancellationToken cancellationToken)
        {
            var newFileName = sourceFileName.Replace(oldName, newName);
            if (!newFileName.Equals(sourceFileName, System.StringComparison.OrdinalIgnoreCase))
                await MoveFileAsync(sourceFileName, newFileName, false, cancellationToken).ConfigureAwait(false);
        }

        #endregion
        #region RenameFolderAsync

        public async Task RenameFolderAsync(string sourceFolderName, string oldName, string newName, bool recursive)
        {
            await RenameFolderAsync(sourceFolderName, oldName, newName, recursive, CancellationToken.None).ConfigureAwait(false);
        }
        public async Task RenameFolderAsync(string sourceFolderName, string oldName, string newName, bool recursive,
            CancellationToken cancellationToken)
        {
            var newFolderName = sourceFolderName.Replace(oldName, newName);
            if (!newFolderName.Equals(sourceFolderName, System.StringComparison.OrdinalIgnoreCase))
                await MoveFolderAsync(sourceFolderName, newFolderName, false).ConfigureAwait(false);
            if (recursive)
            {
                var folders = await GetFolderNamesAsync(newFolderName, null, Sidvall.IO.SearchOption.TopDirectoryOnly, cancellationToken).ConfigureAwait(false);
                foreach (var subFolder in folders)
                {
                    cancellationToken.ThrowIfCancellationRequested();
                    await RenameFolderAsync(subFolder, oldName, newName, recursive, cancellationToken).ConfigureAwait(false);
                }
            }
        }

        #endregion

        #region ToString

        public override string ToString()
        {
            return "StorageManager - IStorage";
        }

        #endregion

        #endregion
        #region Private Members

        internal const string DefaultSearchPattern = "*";

        #region GetPath

        private string GetPath(string rootPath, string path)
        {
            if (string.IsNullOrWhiteSpace(rootPath))
                return path;
            if (string.IsNullOrWhiteSpace(path))
                return rootPath;
            if (System.IO.Path.IsPathRooted(path))
                return path;
            return this.Storage.Combine(rootPath, path);
        }

        #endregion

        #endregion
        #region Constructors

        public StorageManager(IStorage storage)
        {
            this.Storage = storage;
        }

        #endregion
    }
}
